🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / data / src / commonMain / kotlin / org / meshtastic / core / data / manager / MqttManagerImpl.kt
Displaying Raw • Download
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt 2d20cd8a4708e2ef66e98d5259f3a97ec93f240a (2d20cd8a) Text, 10.07 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.data.manager
Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787co.touchlab.kermit.Severity
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.Job
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.SharingStarted
Tff7b72import T7ee787kotlinx.coroutines.flow.StateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.asStateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.catch
Tff7b72import T7ee787kotlinx.coroutines.flow.combine
Tff7b72import T7ee787kotlinx.coroutines.flow.launchIn
Tff7b72import T7ee787kotlinx.coroutines.flow.onEach
Tff7b72import T7ee787kotlinx.coroutines.flow.stateIn
Tff7b72import T7ee787org.koin.core.annotation.Named
Tff7b72import T7ee787org.koin.core.annotation.Single
Tff7b72import T7ee787org.meshtastic.core.common.util.safeCatchingAll
Tff7b72import T7ee787org.meshtastic.core.model.MqttConnectionState
Tff7b72import T7ee787org.meshtastic.core.model.MqttProbeStatus
Tff7b72import T7ee787org.meshtastic.core.network.repository.MQTTRepository
Tff7b72import T7ee787org.meshtastic.core.network.repository.MQTT_KEEPALIVE_SECONDS
Tff7b72import T7ee787org.meshtastic.core.network.repository.isCredentialRejection
Tff7b72import T7ee787org.meshtastic.core.network.repository.mqttTlsConfig
Tff7b72import T7ee787org.meshtastic.core.network.repository.resolveEndpoint
Tff7b72import T7ee787org.meshtastic.core.repository.MqttManager
Tff7b72import T7ee787org.meshtastic.core.repository.NodeRepository
Tff7b72import T7ee787org.meshtastic.core.repository.PacketHandler
Tff7b72import T7ee787org.meshtastic.core.repository.ServiceStateWriter
Tff7b72import T7ee787org.meshtastic.core.resources.Res
Tff7b72import T7ee787org.meshtastic.core.resources.getStringSuspend
Tff7b72import T7ee787org.meshtastic.core.resources.mqtt_error_connection_lost
Tff7b72import T7ee787org.meshtastic.core.resources.mqtt_error_credentials_rejected
Tff7b72import T7ee787org.meshtastic.core.resources.mqtt_error_proxy_failed
Tff7b72import T7ee787org.meshtastic.core.resources.mqtt_error_rejected
Tff7b72import T7ee787org.meshtastic.core.resources.unknown
Tff7b72import T7ee787org.meshtastic.mqtt.ConnectionState
Tff7b72import T7ee787org.meshtastic.mqtt.MqttClient
Tff7b72import T7ee787org.meshtastic.mqtt.MqttException
Tff7b72import T7ee787org.meshtastic.mqtt.ProbeResult
Tff7b72import T7ee787org.meshtastic.mqtt.plus
Tff7b72import T7ee787org.meshtastic.mqtt.probe
Tff7b72import T7ee787org.meshtastic.mqtt.transport.tcp.TcpTransportFactory
Tff7b72import T7ee787org.meshtastic.mqtt.transport.ws.WebSocketTransportFactory
Tff7b72import T7ee787org.meshtastic.proto.MqttClientProxyMessage
Tff7b72import T7ee787org.meshtastic.proto.ToRadio
Tff7b72import T7ee787kotlin.uuid.Uuid
Tf0883e@Single
Tff7b72class T56d364MqttManagerImplTb4b4b4(
Tff7b72private Tff7b72val Te6edf3mqttRepositoryTb4b4b4: Te6edf3MQTTRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3packetHandlerTb4b4b4: Te6edf3PacketHandlerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3serviceStateWriterTb4b4b4: Te6edf3ServiceStateWriterTb4b4b4,
Tff7b72private Tff7b72val Te6edf3nodeRepositoryTb4b4b4: Te6edf3NodeRepositoryTb4b4b4,
Tf0883e@NamedTb4b4b4(Ta5d6ff"Ta5d6ffServiceScopeTa5d6ff"Tb4b4b4) Tff7b72private Tff7b72val Te6edf3scopeTb4b4b4: Te6edf3CoroutineScopeTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3MqttManager Tb4b4b4{
Tff7b72private Tff7b72var Te6edf3mqttMessageFlowTb4b4b4: Te6edf3Job? Tff7b72= Tff7b72null
Tff7b72private Tff7b72val Te6edf3_proxyActive Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)
Tff7b72override Tff7b72val Te6edf3proxyActiveTb4b4b4: Te6edf3StateFlowTff7b72<Tffa657BooleanTff7b72> Tff7b72= Te6edf3_proxyActiveTb4b4b4.Te6edf3asStateFlowTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3mqttConnectionStateTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3MqttConnectionStateTff7b72> Tff7b72=
Te6edf3combineTb4b4b4(Te6edf3_proxyActiveTb4b4b4, Te6edf3mqttRepositoryTb4b4b4.Te6edf3connectionStateTb4b4b4) Tb4b4b4{ Te6edf3activeTb4b4b4, Te6edf3libState Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3activeTb4b4b4) Te6edf3MqttConnectionStateTb4b4b4.Te6edf3Inactive Tff7b72else Te6edf3libStateTb4b4b4.Te6edf3toAppStateTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4.Te6edf3stateInTb4b4b4(Te6edf3scopeTb4b4b4, Te6edf3SharingStartedTb4b4b4.Te6edf3EagerlyTb4b4b4, Te6edf3MqttConnectionStateTb4b4b4.Te6edf3InactiveTb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffstartProxyTb4b4b4(Te6edf3enabledTb4b4b4: Tffa657BooleanTb4b4b4, Te6edf3proxyToClientEnabledTb4b4b4: Tffa657BooleanTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3mqttMessageFlowTff7b72?.Te6edf3isActive Tff7b72=Tff7b72= Tff7b72trueTb4b4b4) Tff7b72return
Tff7b72if Tb4b4b4(Te6edf3enabled Tff7b72&Tff7b72& Te6edf3proxyToClientEnabledTb4b4b4) Tb4b4b4{
Te6edf3_proxyActiveTb4b4b4.Te6edf3value Tff7b72= Tff7b72true
Te6edf3mqttMessageFlow Tff7b72=
Te6edf3mqttRepositoryTb4b4b4.Te6edf3proxyMessageFlow
Tb4b4b4.Te6edf3onEach Tb4b4b4{ Te6edf3message Tff7b72-Tff7b72> Te6edf3packetHandlerTb4b4b4.Te6edf3sendToRadioTb4b4b4(Te6edf3ToRadioTb4b4b4(Te6edf3mqttClientProxyMessage Tff7b72= Te6edf3messageTb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3catch Tb4b4b4{ Te6edf3throwable Tff7b72-Tff7b72>
Te6edf3_proxyActiveTb4b4b4.Te6edf3value Tff7b72= Tff7b72false
T8b949e// safeCatchingAll swallows the Skiko ExceptionInInitializerError that
T8b949e// compose-resources raises on headless JVM tests; production resolves the
T8b949e// localized string and the error is still surfaced either way.
Tff7b72val Te6edf3message Tff7b72=
Te6edf3safeCatchingAll Tb4b4b4{
Tff7b72when Tb4b4b4{
Te6edf3throwable Tff7b72is Te6edf3MqttExceptionTb4b4b4.Te6edf3ConnectionRejected Tff7b72&Tff7b72&
Te6edf3throwableTb4b4b4.Te6edf3isCredentialRejectionTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72>
Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3mqtt_error_credentials_rejectedTb4b4b4)
Te6edf3throwable Tff7b72is Te6edf3MqttExceptionTb4b4b4.Te6edf3ConnectionRejected Tff7b72-Tff7b72>
Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3mqtt_error_rejectedTb4b4b4, Te6edf3throwableTb4b4b4.Te6edf3detailTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3throwable Tff7b72is Te6edf3MqttExceptionTb4b4b4.Te6edf3ConnectionLost Tff7b72-Tff7b72>
Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3mqtt_error_connection_lostTb4b4b4)
Tff7b72else Tff7b72-Tff7b72> Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3mqtt_error_proxy_failedTb4b4b4, Te6edf3throwableTb4b4b4.Te6edf3detailTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3getOrDefaultTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3serviceStateWriterTb4b4b4.Te6edf3setErrorMessageTb4b4b4(Te6edf3text Tff7b72= Te6edf3messageTb4b4b4, Te6edf3severity Tff7b72= Te6edf3SeverityTb4b4b4.Te6edf3WarnTb4b4b4)
Tb4b4b4}
Tb4b4b4.Te6edf3launchInTb4b4b4(Te6edf3scopeTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffstopTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3mqttMessageFlowTff7b72?.Te6edf3isActive Tff7b72=Tff7b72= Tff7b72trueTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffStopping MqttClientProxyTa5d6ff" Tb4b4b4}
Te6edf3mqttMessageFlowTff7b72?.Te6edf3cancelTb4b4b4(Tb4b4b4)
Te6edf3mqttMessageFlow Tff7b72= Tff7b72null
Tb4b4b4}
Te6edf3_proxyActiveTb4b4b4.Te6edf3value Tff7b72= Tff7b72false
Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffhandleMqttProxyMessageTb4b4b4(Te6edf3messageTb4b4b4: Te6edf3MqttClientProxyMessageTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3topic Tff7b72= Te6edf3messageTb4b4b4.Te6edf3topic
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ff[mqttClientProxyMessage] Tffd700$Te6edf3topicTa5d6ff" Tb4b4b4}
Tff7b72val Te6edf3retained Tff7b72= Te6edf3messageTb4b4b4.Te6edf3retained Tff7b72=Tff7b72= Tff7b72true
Tff7b72when Tb4b4b4{
Te6edf3messageTb4b4b4.Te6edf3text Tff7b72!Tff7b72= Tff7b72null Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3mqttRepositoryTb4b4b4.Te6edf3publishTb4b4b4(Te6edf3topicTb4b4b4, Te6edf3messageTb4b4b4.Te6edf3textTff7b72!!Tb4b4b4.Te6edf3encodeToByteArrayTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3retainedTb4b4b4)
Tb4b4b4}
Te6edf3messageTb4b4b4.Te6edf3data_ Tff7b72!Tff7b72= Tff7b72null Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3mqttRepositoryTb4b4b4.Te6edf3publishTb4b4b4(Te6edf3topicTb4b4b4, Te6edf3messageTb4b4b4.Te6edf3data_Tff7b72!!Tb4b4b4.Te6edf3toByteArrayTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3retainedTb4b4b4)
Tb4b4b4}
Tff7b72else Tff7b72-Tff7b72> Tb4b4b4{Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72fun Te6edf3ConnectionStateTb4b4b4.Td2a8fftoAppStateTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3MqttConnectionState Tff7b72= Tff7b72when Tb4b4b4(Tff7b72thisTb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connecting Tff7b72-Tff7b72> Te6edf3MqttConnectionStateTb4b4b4.Te6edf3Connecting
Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected Tff7b72-Tff7b72> Te6edf3MqttConnectionStateTb4b4b4.Te6edf3Connected
Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Reconnecting Tff7b72-Tff7b72>
Te6edf3MqttConnectionStateTb4b4b4.Te6edf3ReconnectingTb4b4b4(Te6edf3attempt Tff7b72= Te6edf3attemptTb4b4b4, Te6edf3lastError Tff7b72= Te6edf3lastErrorTff7b72?.Te6edf3messageTb4b4b4)
Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Disconnected Tff7b72-Tff7b72>
Te6edf3reasonTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3MqttConnectionStateTb4b4b4.Te6edf3DisconnectedTb4b4b4(Te6edf3reason Tff7b72= Tffa657itTb4b4b4.Te6edf3messageTb4b4b4) Tb4b4b4}
Tff7b72?: Te6edf3MqttConnectionStateTb4b4b4.Te6edf3DisconnectedTb4b4b4.Te6edf3Idle
Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffprobeTb4b4b4(
Te6edf3addressTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3tlsEnabledTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3usernameTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3passwordTb4b4b4: Tffa657String?Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3MqttProbeStatus Tb4b4b4{
Tff7b72val Te6edf3endpoint Tff7b72= Te6edf3resolveEndpointTb4b4b4(Te6edf3addressTb4b4b4, Te6edf3tlsEnabledTb4b4b4)
Tff7b72val Te6edf3result Tff7b72=
Te6edf3MqttClientTb4b4b4.Te6edf3probeTb4b4b4(Te6edf3endpoint Tff7b72= Te6edf3endpointTb4b4b4) Tb4b4b4{
T8b949e// probe() requires a transportFactory in 0.4.0 (errors otherwise); mirror the live client,
T8b949e// including its scoped private-CA trust hook — otherwise a probe would fail where a connect succeeds.
Tff7b72val Te6edf3tls Tff7b72= Te6edf3mqttTlsConfigTb4b4b4(Tb4b4b4)
Te6edf3transportFactory Tff7b72= Te6edf3TcpTransportFactoryTb4b4b4(Te6edf3tlsTb4b4b4) Tff7b72+ Te6edf3WebSocketTransportFactoryTb4b4b4(Te6edf3tlsTb4b4b4)
T8b949e// Mirror the live client's keepalive too: the library default is 0 (no keepalive),
T8b949e// which some brokers reject — misleadingly, as CLIENT_IDENTIFIER_NOT_VALID.
Te6edf3keepAliveSeconds Tff7b72= Te6edf3MQTT_KEEPALIVE_SECONDS
T8b949e// Per-connection random suffix: myId identifies the node (and is null →
T8b949e// "unknown" before the node record loads), so two probes can collide on one
T8b949e// client-id and evict each other (SESSION_TAKEN_OVER). See MQTTRepositoryImpl.
Te6edf3clientId Tff7b72= Ta5d6ff"Ta5d6ffMeshtasticAndroidMqttProbe-Tffd700${Te6edf3nodeRepositoryTb4b4b4.Te6edf3myIdTb4b4b4.Te6edf3value Tff7b72?: Ta5d6ff"Te6edf3unknownTa5d6ff"Tffd700}Ta5d6ff-Tffd700${Te6edf3UuidTb4b4b4.Te6edf3randomTb4b4b4(Tb4b4b4)Tffd700}Ta5d6ff"
Tff7b72val Te6edf3user Tff7b72= Te6edf3usernameTff7b72?.Te6edf3takeUnless Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3pass Tff7b72= Te6edf3passwordTff7b72?.Te6edf3takeUnless Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4) Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3user Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tff7b72thisTb4b4b4.Te6edf3username Tff7b72= Te6edf3user
Tff7b72if Tb4b4b4(Te6edf3pass Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Te6edf3passwordTb4b4b4(Te6edf3passTb4b4b4)
Tb4b4b4}
Tff7b72return Te6edf3resultTb4b4b4.Te6edf3toAppStatusTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72fun Te6edf3ProbeResultTb4b4b4.Td2a8fftoAppStatusTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3MqttProbeStatus Tff7b72= Tff7b72when Tb4b4b4(Tff7b72thisTb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3ProbeResultTb4b4b4.Te6edf3Success Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72val Te6edf3info Tff7b72= Te6edf3serverInfo
Tff7b72val Te6edf3summary Tff7b72=
Te6edf3buildList Tb4b4b4{
Te6edf3infoTb4b4b4.Te6edf3assignedClientIdentifierTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3addTb4b4b4(Ta5d6ff"Ta5d6ffclient=Tffd700$Te6edf3itTa5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3infoTb4b4b4.Te6edf3maximumQosOrdinalTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3addTb4b4b4(Ta5d6ff"Ta5d6ffmaxQoS=Tffd700$Te6edf3itTa5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3infoTb4b4b4.Te6edf3serverKeepAliveSecondsTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3addTb4b4b4(Ta5d6ff"Ta5d6ffkeepalive=Tffd700${Tffa657itTffd700}Ta5d6ffsTa5d6ff"Tb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3joinToStringTb4b4b4(Ta5d6ff"Ta5d6ff, Ta5d6ff"Tb4b4b4)
Tb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Tff7b72null Tb4b4b4}
Te6edf3MqttProbeStatusTb4b4b4.Te6edf3SuccessTb4b4b4(Te6edf3serverInfo Tff7b72= Te6edf3summaryTb4b4b4)
Tb4b4b4}
Tff7b72is Te6edf3ProbeResultTb4b4b4.Te6edf3Rejected Tff7b72-Tff7b72>
Te6edf3MqttProbeStatusTb4b4b4.Te6edf3RejectedTb4b4b4(
Te6edf3reasonCode Tff7b72= Te6edf3reasonCodeTb4b4b4.Te6edf3valueTb4b4b4,
Te6edf3reason Tff7b72= Te6edf3messageTb4b4b4,
Te6edf3serverReference Tff7b72= Te6edf3serverReferenceTb4b4b4,
Tb4b4b4)
Tff7b72is Te6edf3ProbeResultTb4b4b4.Te6edf3DnsFailure Tff7b72-Tff7b72> Te6edf3MqttProbeStatusTb4b4b4.Te6edf3DnsFailureTb4b4b4(Te6edf3message Tff7b72= Te6edf3causeTb4b4b4.Te6edf3messageTb4b4b4)
Tff7b72is Te6edf3ProbeResultTb4b4b4.Te6edf3TcpFailure Tff7b72-Tff7b72> Te6edf3MqttProbeStatusTb4b4b4.Te6edf3TcpFailureTb4b4b4(Te6edf3message Tff7b72= Te6edf3causeTb4b4b4.Te6edf3messageTb4b4b4)
Tff7b72is Te6edf3ProbeResultTb4b4b4.Te6edf3TlsFailure Tff7b72-Tff7b72> Te6edf3MqttProbeStatusTb4b4b4.Te6edf3TlsFailureTb4b4b4(Te6edf3message Tff7b72= Te6edf3causeTb4b4b4.Te6edf3messageTb4b4b4)
Tff7b72is Te6edf3ProbeResultTb4b4b4.Te6edf3Timeout Tff7b72-Tff7b72> Te6edf3MqttProbeStatusTb4b4b4.Te6edf3TimeoutTb4b4b4(Te6edf3timeoutMs Tff7b72= Te6edf3durationMsTb4b4b4)
Tff7b72is Te6edf3ProbeResultTb4b4b4.Te6edf3Other Tff7b72-Tff7b72> Te6edf3MqttProbeStatusTb4b4b4.Te6edf3OtherTb4b4b4(Te6edf3message Tff7b72= Te6edf3causeTb4b4b4.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e/** Failure detail for a user-facing message; a throwable without a message still needs a placeholder to substitute. */
Tff7b72private Tff7b72suspend Tff7b72fun Te6edf3ThrowableTb4b4b4.Td2a8ffdetailTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Te6edf3message Tff7b72?: Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3unknownTb4b4b4)
Served by rngit 1.5.0 - Generated in 0.07s